﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Buddy.BehaviorTree;
using Buddy.Common;
using Buddy.Common.Math;
using Buddy.CommonBot;
using Buddy.Navigation;
using Buddy.Swtor;
using Buddy.Swtor.Objects;

using WingIt.Routines;
using WingIt.Routines.Mirrors;

using Action = Buddy.BehaviorTree.Action;


namespace WingIt
{
    [RevisionControlId("$Id: Abilities.cs 686 2012-08-12 16:12:20Z Chinajade $")]
    public static partial class WingItCommon
    {
        /// <summary>
        /// <para>If DOWHEN evaluates to true, breaks crouch or cover by using a brief jog movement.</para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * If DOWHEN is omitted, the behavior will always be attempted.</para></description></item>
        /// <item><description><para> * This behavior throttles failed attempts to break crouch so other activities can take place.
        /// When throttling happens, a message is emitted to the log.</para></description></item>
        /// <item><description><para> * Failure to break crouch can happen due to lag, client stuttering, low frame rates, game terrain, and other things.</para></description></item>
        /// </list></para>
        /// </summary>
        /// <returns><para>a behavior tree Composite for use in a PrioritySelector.</para>
        /// <para>The Composite returns RunStatus.Success to the BT parent if the behavior is in progress.
        /// Otherwise, returns RunStatus.Failure indicating no action was necessary.</para>
        /// </returns>
        ///  7Jul2012-20:26UTC chinajade
        public static Composite BreakCrouchOrCover(CanRunDecoratorDelegate doWhen = null)
        {
            doWhen = doWhen ?? (ctx => true);       // Resolve optional argument to something sane

            return new Decorator(whenMovementEnabled => doWhen(whenMovementEnabled) && LazyRaider.IsMovementEnabled && Me.IsInCover(),
                new Action(breakCover => Buddy.Swtor.Movement.Move(MovementDirection.Backward, TimeSpan.FromMilliseconds(1))));
        }

        /// <summary>
        /// <para>(BETA--doesn't work ATM) If DOWHEN evaluates to true, toggles Stealth off if it is in use.</para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * If DOWHEN is omitted, the behavior will always be attempted.</para></description></item>
        /// <item><description><para> * This behavior throttles failed attempts to stealth so other activities can take place.
        /// When throttling happens, a message is emitted to the log.</para></description></item>
        /// <item><description><para> * Failure to stealth can happen due to lag, client stuttering, low frame rates, game terrain, and other things.</para></description></item>
        /// </list></para>
        /// </summary>
        /// <returns><para>a behavior tree Composite for use in a PrioritySelector.</para>
        /// <para>The Composite returns RunStatus.Success to the BT parent if the behavior is in progress.
        /// Otherwise, returns RunStatus.Failure indicating no action was necessary.</para>
        /// </returns>
        ///  8Jul2012-18:12UTC chinajade
        public static Composite BreakStealth(CanRunDecoratorDelegate doWhen = null)
        {
            TimeSpan    BreakStealthRetryThrottle        = TimeSpan.FromMilliseconds(750);

            doWhen = doWhen ?? (ctx => true);       // Resolve optional argument to something sane

            return new Decorator(whenStealthed => doWhen(whenStealthed) && Me.HasMirrorBuff("Stealth"),
                new PrioritySelector(
                    // Throttle attempts...
                    new Decorator(whenNotThrottled => breakStealthRetryThrottle.IsReady(),
                        new Action(breakStealth => { 
                            breakStealthRetryThrottle.Used(BreakStealthRetryThrottle);
                            // TODO: Stealth is NOT disabled by toggling the ability a second time.  Need a proper BW API to turn it off.
                            //Mirror.Cast("Stealth");
                            })),

                    // This behavior is not done until we can meet our exit criteria (no longer in stealth)...
                    // We can get this far in the PrioritySelector, if the behavior was throttled.
                    new Action(alwaysSucceed => { return (RunStatus.Success); })
                    ));
        }
        public static Throttle breakStealthRetryThrottle = new Throttle_Timed("BreakStealthRetry");

        /// <summary>
        /// <para>Guards current companion, if not already guarded and DOWHEN allows it.</para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * If DOWHEN is omitted, guard is always allowed.</para></description></item>
        /// <item><description><para> * This behavior is a no-op if the companion is not in use or dead, or the "Guard" ability is unknown.</para></description></item>
        /// <item><description><para> * The caller's decision on whether or not to guard a companion may be influenced by your group makeup.</para></description></item>
        /// </list></para>
        /// </summary>
        /// <param name="doWhen"></param>
        /// <returns></returns>
        /// 15Jul2012-23:25UTC chinajade
        public static Composite ApplyGuard(CanRunDecoratorDelegate doWhen = null)
        {
            doWhen = doWhen ?? (ctx => true);       // Resolve optional agument to something sane

            return (new Decorator(whenNotGuarded => doWhen(whenNotGuarded) && (TargetSelect.ForGuard() != null)
                                         && !TargetSelect.ForGuard().HasMirrorBuff("Guard") && !TargetSelect.ForGuard().IsDead
                                         && Me.HasMirrorAbility("Guard"),
                new Sequence(
                    Mirror.Cast("Guard", castGuardOn => TargetSelect.ForGuard()),
                    new Action(logAction => Logger.Write("{0} now guarded.", TargetSelect.ForGuard().LoggableIdentifier()))
                    )
                ));
        }

        /// <summary Debuff>
        /// <para>(Still Beta).</para>
        /// <para>Uses ABILITYNAME to debuff any target that is engaged that does not have DEBUFFNAME on it.</para>
        /// </summary>
        /// <param name="debuffName"></param>
        /// <param name="requirements"></param>
        /// <returns></returns>
        /// Neo93
        public static Composite DebuffAllTargets(string abilityName, string debuffName, BooleanValueDelegate requirements = null)
        {
            Logger.ContractRequires(() => !string.IsNullOrEmpty(abilityName), () => "!string.IsNullOrEmpty(debuffName)");
            Logger.ContractRequires(() => !string.IsNullOrEmpty(debuffName), () => "!string.IsNullOrEmpty(debuffOnTargetName)");

            string mirrorAbilityName = Mirror.LookupAbilityName(abilityName);
            string mirrorDebuffName = Mirror.LookupDebuffName(debuffName);

            requirements = requirements ?? (c => true);  // Resolve a null argument into something sane

            return Mirror.Cast(mirrorAbilityName, debuffOn => TargetSelect.ForDebuff(), debuffWhen => requirements(debuffWhen) && (TargetSelect.ForDebuff(mirrorDebuffName) != null));
        }
    }
}
